package org.lightdi.container.parser;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import org.lightdi.container.DIContainer;
import org.lightdi.container.config.ComponentConfig;
import org.lightdi.container.config.ContainerConfig;
import org.lightdi.container.config.ComponentConfig.Arg;
import org.lightdi.container.config.ComponentConfig.ConstructorArg;
import org.lightdi.container.config.ComponentConfig.InitMethod;
import org.lightdi.container.config.ComponentConfig.SetterArg;
import org.lightdi.container.security.ContainerLockKey;
import org.lightdi.container.tag.ComponentTag;
import org.lightdi.container.tag.ComponentsTag;
import org.lightdi.container.tag.ContainerTag;
import org.lightdi.container.tag.DeleteComponentsTag;
import org.lightdi.container.tag.InsertComponentsTag;
import org.lightdi.container.tag.LightDIConfigTag;
import org.lightdi.container.tag.UpdateComponentsTag;
import org.lightdi.container.util.LightDILoggerUtil;
import org.lightdi.util.ObjectUtil;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class ConfigLoadHandler extends DefaultHandler {

	private Map<String, ContainerConfig> containerConfigs = new HashMap<String, ContainerConfig>();
	private Map<String, ContainerConfig> includedConfigXMLs = new ConcurrentHashMap<String, ContainerConfig>();

	private String loggingType = LightDIConfigTag.Attributes.LoadingLog.DEFAULT;
	private String currentConfigXML;
	private ContainerConfig currentContainer;
	private ComponentConfig currentComponent;

	private boolean isUpdateComponentsMode = false;
	private boolean isDeleteComponentsMode = false;
	private boolean isInsertComponentsMode = false;

	private Object argBindingTarget = null;

	private static final Logger logger = Logger
			.getLogger(DIContainer.DEFAULT_CONTAINER_NAME);

	@Override
	public void startElement(String uri, String localName, String name,
			Attributes attributes) throws SAXException {
		if (name.equals(LightDIConfigTag.ROOT)) {
			String loggingType = attributes
					.getValue(LightDIConfigTag.Attributes.LOADING_LOG);
			if (loggingType == null)
				loggingType = LightDIConfigTag.Attributes.LoadingLog.CONTAINERS_ONLY;
			this.loggingType = loggingType;

		} else if (name.equals(ContainerTag.ROOT)) {

			// read container
			// path setting container
			if (attributes.getValue(ContainerTag.Attributes.PATH) != null) {
				// read new file
				ContainerConfig containerConfig = new ContainerConfig();
				containerConfig = ConfigLoadUtil.readContainerConfig(
						containerConfig, attributes);
				containerConfig.setLoaded(false);

				// container lock
				if (attributes.getValue(ContainerTag.Attributes.LOCKED) != null) {
					ContainerLockKey lockKey = new ContainerLockKey();
					String passKey = lockKey.generatePassKey();
					LightDILoggerUtil
							.info(loggingType, logger, containerConfig
									.getName(), null, "container lock key : "
									+ passKey);
					containerConfig.setLockKey(lockKey);
				}

				includedConfigXMLs.put(containerConfig.getPath(),
						containerConfig);
				containerConfigs
						.put(containerConfig.getName(), containerConfig);

				String containerName = containerConfig.getName();
				String msg = "Include" + LightDILoggerUtil.START;
				LightDILoggerUtil.info(loggingType, logger, containerName,
						null, msg);
				return;

			} else {
				// new container or not
				String containerName = attributes
						.getValue(ContainerTag.Attributes.NAME);
				if (containerName == null)
					containerName = DIContainer.DEFAULT_CONTAINER_NAME;
				ContainerConfig containerConfig = ConfigLoadUtil
						.getRegisteredOrNewContainerConfig(containerConfigs,
								containerName);
				containerConfig = ConfigLoadUtil.readContainerConfig(
						containerConfig, attributes);

				containerConfig.setLoaded(true);
				includedConfigXMLs.put(currentConfigXML, containerConfig);
				containerConfigs
						.put(containerConfig.getName(), containerConfig);

				currentContainer = containerConfig;

				String msg = "Load" + LightDILoggerUtil.START;
				LightDILoggerUtil.info(loggingType, logger, containerName,
						null, msg);

				// container lock
				if (attributes.getValue(ContainerTag.Attributes.LOCKED) != null) {
					ContainerLockKey lockKey = new ContainerLockKey();
					String passKey = lockKey.generatePassKey();
					LightDILoggerUtil.info(loggingType, logger,
							currentContainer.getName(), null,
							"container lock key : " + passKey);
					currentContainer.setLockKey(lockKey);
				}

				return;

			}

		} else if (name.equals(ComponentsTag.ROOT)) {
			// component list
			ContainerConfig xmlConfig = includedConfigXMLs
					.get(currentConfigXML);
			xmlConfig.setLoaded(true);
			includedConfigXMLs.put(currentConfigXML, xmlConfig);
			containerConfigs.put(xmlConfig.getName(), xmlConfig);

		} else if (name.equals(ComponentTag.ROOT)) {
			// component
			Map<String, ComponentConfig> components = currentContainer
					.getComponents();

			// get already registered component
			String componentName = attributes
					.getValue(ComponentTag.Attributes.NAME);
			// edit registerd component
			ComponentConfig registered = components.get(componentName);
			if (registered != null) {
				if (isUpdateComponentsMode) {
					currentComponent = ConfigLoadUtil.readComponentConfig(
							new ComponentConfig(), attributes);
					currentContainer.getComponents().put(componentName,
							currentComponent);

					String containerName = currentContainer.getName();
					String msg = "Update" + LightDILoggerUtil.START;
					LightDILoggerUtil.info(loggingType, logger, containerName,
							componentName, msg);
					return;

				} else if (isDeleteComponentsMode) {
					components.remove(componentName);

					String containerName = currentContainer.getName();
					String msg = "Delete" + LightDILoggerUtil.START;
					LightDILoggerUtil.info(loggingType, logger, containerName,
							componentName, msg);
					return;

				} else if (isInsertComponentsMode) {
					// insertComponents is following
					throw new IllegalArgumentException(
							"The component to insert has been already registered! : "
									+ componentName);
				}
			} else {
				if (isUpdateComponentsMode)
					throw new IllegalArgumentException(
							"The component to update has not been registered! : "
									+ componentName);
				else if (isDeleteComponentsMode)
					throw new IllegalArgumentException(
							"The component to delete has not been registered! : "
									+ componentName);

				currentComponent = ConfigLoadUtil.readComponentConfig(
						new ComponentConfig(), attributes);
				currentContainer.getComponents().put(componentName,
						currentComponent);

				String containerName = currentContainer.getName();
				String msg = "Register" + LightDILoggerUtil.START;
				LightDILoggerUtil.info(loggingType, logger, containerName,
						componentName, msg);
				return;
			}

		} else if (name.equals(ComponentTag.ArgTag.ROOT)) {
			// TODO
			// arg(init method, constructor, setter)
			if (argBindingTarget instanceof InitMethod) {
				Arg config = ConfigLoadUtil
						.readArgConfig(new Arg(), attributes);
				InitMethod target = (InitMethod) argBindingTarget;
				target.getArgs().add(config);
			} else {
				throw new UnsupportedOperationException("TODO");
			}

		} else if (name.equals(ComponentTag.InitMethodTag.ROOT)) {
			// init method
			InitMethod config = ConfigLoadUtil.readInitMethodConfig(
					new InitMethod(), attributes);
			currentComponent.getInitMethods().add(config);
			argBindingTarget = config;
			return;

		} else if (name.equals(ComponentTag.ConstructorArgTag.ROOT)) {
			// constructor arg
			ConstructorArg config = ConfigLoadUtil.readConstructorArgConfig(
					new ConstructorArg(), attributes);
			currentComponent.getConstructorArgs().add(config);
			argBindingTarget = config;
			return;

		} else if (name.equals(ComponentTag.SetterArgTag.ROOT)) {
			// setter arg
			SetterArg config = ConfigLoadUtil.readSetterArgConfig(
					new SetterArg(), attributes);
			currentComponent.getSetterArgs().add(config);
			argBindingTarget = config;
			return;

		} else if (name.equals(UpdateComponentsTag.ROOT)) {
			// update components
			isUpdateComponentsMode = true;
			String containerName = currentContainer.getName();
			String msg = "Update Components" + LightDILoggerUtil.START;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			return;

		} else if (name.equals(DeleteComponentsTag.ROOT)) {
			// delete components
			isDeleteComponentsMode = true;
			String containerName = currentContainer.getName();
			String msg = "Delete Components" + LightDILoggerUtil.START;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			return;

		} else if (name.equals(InsertComponentsTag.ROOT)) {
			// insert components
			isInsertComponentsMode = true;
			String containerName = currentContainer.getName();
			String msg = "Insert Components" + LightDILoggerUtil.START;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			return;
		}
	}

	@Override
	public void endElement(String uri, String localName, String name)
			throws SAXException {
		if (name.equals(ContainerTag.ROOT)) {
			if (currentContainer != null) {
				ContainerConfig config = ObjectUtil.deepCopy(currentContainer);
				containerConfigs.put(config.getName(), config);
			}
			String containerName = currentContainer.getName();
			String msg = "Load" + LightDILoggerUtil.END;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			return;

		} else if (name.equals(ComponentTag.ROOT)) {
			if (currentComponent != null) {
				ComponentConfig config = ObjectUtil.deepCopy(currentComponent);
				currentContainer.getComponents().put(config.getName(), config);

				String containerName = currentContainer.getName();
				String componentName = currentComponent.getName();
				String msg = "Register" + LightDILoggerUtil.END;
				LightDILoggerUtil.info(loggingType, logger, containerName,
						componentName, msg);
			}
			return;

		} else if (name.equals(UpdateComponentsTag.ROOT)) {
			isUpdateComponentsMode = false;
			String containerName = currentContainer.getName();
			String msg = "Update Components" + LightDILoggerUtil.END;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			currentComponent = null;
			return;

		} else if (name.equals(DeleteComponentsTag.ROOT)) {
			isDeleteComponentsMode = false;
			String containerName = currentContainer.getName();
			String msg = "Delete Components" + LightDILoggerUtil.END;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			currentComponent = null;
			return;

		} else if (name.equals(InsertComponentsTag.ROOT)) {
			isInsertComponentsMode = false;
			String containerName = currentContainer.getName();
			String msg = "Insert Components" + LightDILoggerUtil.END;
			LightDILoggerUtil.info(loggingType, logger, containerName, null,
					msg);
			currentComponent = null;
			return;

		}
	}

	public Map<String, ContainerConfig> getContainerConfigs() {
		return containerConfigs;
	}

	public void setContainerConfigs(
			Map<String, ContainerConfig> containerConfigs) {
		this.containerConfigs = containerConfigs;
	}

	public Map<String, ContainerConfig> getIncludedConfigXMLs() {
		return includedConfigXMLs;
	}

	public void setIncludedConfigXMLs(
			Map<String, ContainerConfig> includedConfigXMLs) {
		this.includedConfigXMLs = includedConfigXMLs;
	}

	/**
	 * @param currentContainer
	 *            the currentContainer to set
	 */
	public void setCurrentContainer(ContainerConfig currentContainer) {
		this.currentContainer = currentContainer;
	}

	/**
	 * @return the currentContainer
	 */
	public ContainerConfig getCurrentContainer() {
		return currentContainer;
	}

	public String getCurrentConfigXML() {
		return currentConfigXML;
	}

	public void setCurrentConfigXML(String currentConfigXML) {
		this.currentConfigXML = currentConfigXML;
	}

}
